import "babel-polyfill";
import {islice} from "../algorithm/array";
import {range} from "../algorithm/iterator";

export var permutations = function*(r) {
    var n = this.length;
    if (arguments.length === 0) {
        r = n;
    }
    if (n == 0 || r > n) {
        return;
    }
    var indices = [...range(n)];
    var cycles = [...range(n, n - r, -1)];
    yield [...indices::islice(0, r)::map(x => this[x])];
    var finish;
    do {
        finish = true;
        for (let i = r; --i >= 0;) {
            if (--cycles[i]) {
                let j = cycles[i];
                [indices[i], indices[n - j]] = [indices[n - j], indices[i]];
                yield [...indices::islice(0, r)::map(x => this[x])];
                finish = false;
                break;
            } else {
                indices.push(indices.splice(i, 1)[0]);
                cycles[i] = n - i;
            }
        }
    } while (!finish);
};

export var combinations = function*(r, repeatable = false) {
    var n = this.length;
    if (repeatable) {
        let indices = new Array(r).fill(0);
        yield indices.map(x => this[x]);
        let finish;
        do {
            finish = true;
            for (let i = r; --i >= 0;) {
                if (indices[i] !== n - 1) {
                    indices.fill(indices[i] + 1, i);
                    yield indices.map(x => this[x]);
                    finish = false;
                    break;
                }
            }
        } while (!finish);
    } else {
        if (r > n) {
            return;
        }
        let indices = [...range(r)];
        yield indices.map(x => this[x]);
        let finish;
        do {
            finish = true;
            for (let i = r; --i >= 0;) {
                if (indices[i] !== i + n - r) {
                    ++indices[i];
                    for (let j = i + 1; j < r; ++j) {
                        indices[j] = indices[j - 1] + 1;
                    }
                    yield indices.map(x => this[x]);
                    finish = false;
                    break;
                }
            }
        } while (!finish);
    }
};

export var powerSet = function*() {
    for (let r = 0, n = this.length; r <= n; ++r) {
        yield* this::combinations(r);
    }
};
